//-----------------------------------------------------
// Design Name : 
// File Name : 
// Function : 
//-----------------------------------------------------
module Decode (
	input Clk,
	input [3:0] opCode, 
	input [3:0] cond,
	input [3:0] extendedOpCode,
	input [15:0] flag,
	input reset,
//	output reg branch_mux, // 1 = branch, 0 = jump
	output reg [2:0] ALUOp,
	output reg memEnable,
	output reg memWrite,
	output reg IRWrite,
	output reg regWrite,
	output reg ALUSrcA,
	output reg [1:0] ALUSrcB,
	output reg MemtoReg,
	output reg PCEnable,
	output reg zeroExtend,
	output reg shiftImmediate,
	output reg ALUOutSelect,
	output reg [4:0] flagsEnable,
	output reg shiftSelect,
	output reg immedateALUSrcA,
	output reg JALSelect,
	output reg jump,
	output reg LUIEnable
); 
//From Most significant to least significant bit
//  these are C, L, F, Z, N
wire [4:0] PSRCodes = {flag[15],flag[13],flag[10],flag[9],flag[8]};

// Op Codes
//parameter RTYPE = 4'b0000; // These are same as state encoding for convenience
//parameter SPECIAL = 4'b0100;
//parameter SHIFT = 4'b1000;
//parameter BCOND = 4'b1100;
parameter ANDI = 4'b0001;
parameter ADDI = 4'b0101;
parameter SUBI = 4'b1001;
parameter MOVI = 4'b1101;
parameter ORI = 4'b0010;
parameter XORI = 4'b0011;
parameter CMPI = 4'b1011;
parameter LUI = 4'b1111;

//Extended Op Codes
parameter AND = 4'b0001;
parameter ADD = 4'b0101;
parameter SUB = 4'b1001;
parameter MOV = 4'b1101;
parameter OR = 4'b0010;
parameter XOR = 4'b0011;
parameter CMP = 4'b1011;
parameter LOAD = 4'b0000;
parameter STOR = 4'b0100;
parameter JAL = 4'b1000;
parameter Jcond = 4'b1100;

//State encoding
parameter FETCH = 4'b0010;
parameter RTYPE = 4'b0000;   // Same as op code for convenience
parameter SPECIAL = 4'b0100; // Same as op code for convenience
parameter SHIFT = 4'b1000;   // Same as op code for convenience
parameter BCOND = 4'b1100;   // Same as op code for convenience
parameter REGWRITE = 4'b0110;
parameter LOADEX = 4'b0111;
parameter STOREX = 4'b0011;
parameter JALEX = 4'b1001;
parameter JcondEX = 4'b1010;
parameter SHIFTWRITE = 4'b1011;
parameter BcondEX = 4'b0101;
parameter MEMWRITE = 4'b1110;
parameter PCWRITE = 4'b1111;
parameter Instruction = 4'b0001;
parameter blockRamWrite = 4'b1101;

reg [3:0] state, nextstate;
reg		 PC_branch;
//State register
always @(posedge Clk)
	if(reset) state <= FETCH;
	else state <= nextstate;
	
//Next state logic
always @(state, opCode, extendedOpCode, nextstate)
	begin
		case(state)
			FETCH: nextstate <= Instruction;
			
			Instruction:
						case(opCode)
							RTYPE: nextstate <= RTYPE;
							SPECIAL: 
								case(extendedOpCode)
									LOAD: 	nextstate <= LOADEX;
									STOR: 	nextstate <= STOREX;
									JAL: 		nextstate <= JALEX;
									Jcond: 	nextstate <= JcondEX;
									default: nextstate <= FETCH;
								endcase
							SHIFT: nextstate <= SHIFT;
							BCOND: nextstate <= BCOND;
							default: nextstate <= RTYPE; // Immediate goes this way
						endcase
			RTYPE: 		nextstate <= FETCH;
			JALEX: 		nextstate <= FETCH;
			SHIFT: 		nextstate <= FETCH;
			LOADEX: 		nextstate <= blockRamWrite;
			blockRamWrite: nextstate <= FETCH;
			STOREX:		nextstate <= FETCH;
			BCOND: 		nextstate <= FETCH;
			JcondEX:		nextstate <= FETCH;
			default: 	nextstate <= FETCH;
		endcase
	end
	
always @(*)
	begin
		ALUOp <= 3'b001;
		regWrite <= 0;
		memEnable <= 0;
		memWrite <= 0;
		ALUSrcA <= 1;
		ALUSrcB <= 2'b00;
		//branch_mux <= 0; // 1 = branch, 0 = jump
		IRWrite <= 0;
		MemtoReg <= 0;
		PCEnable <= 0;
		zeroExtend <= 0;
		shiftImmediate <= 0;
		ALUOutSelect <= 0;
		shiftSelect <= 0;
		flagsEnable <= 5'b00000;
		immedateALUSrcA <= 0;
		JALSelect <= 0;
		jump <= 0;
		LUIEnable <= 0;
		case(state)
			FETCH:
			begin
				PCEnable <= 1;
				ALUSrcA <= 0;
				ALUSrcB <= 2'b01;
				ALUOp <= 3'b010;
				memEnable <= 1;
				IRWrite <= 1;
			end
			//  flags are C, L, F, Z, N
			RTYPE: 
			begin
					regWrite <= 1;
				if(opCode) // If it is an immediate instruction - this makes
					begin
						//ALUSrcB <= 2'b10;
						immedateALUSrcA <= 1;
						case(opCode)
							ADDI :
								begin
									ALUOp <= 3'b010;
									flagsEnable <= 5'b10100;
								end
							ANDI  : ALUOp <= 3'b000;
							XORI :
								begin
								ALUOp <= 3'b011;
								zeroExtend <= 1;
								end
							CMPI :
							begin							
							flagsEnable <= 5'b01011;
							regWrite<= 0;
							end
							LUI  : 
								begin
									LUIEnable <= 1;
									immedateALUSrcA <= 1;
									ALUSrcB <= 2'b11;
								end
							SUBI : 
								begin
									ALUOp <= 3'b110;
									flagsEnable <= 5'b10100;
								end
							MOVI : 
								begin
									ALUSrcB <= 2'b11;
									immedateALUSrcA <= 1;
								end
							ORI:
								begin
									zeroExtend <= 1;
								end
						endcase
						
					end
				else
					begin
						//alusrcb <= 00;
						case(extendedOpCode)
							ADD  :  
								begin
									ALUOp <= 3'b010;
									flagsEnable <= 5'b10100;
								end
							SUB  :  
								begin
									ALUOp <= 3'b110;
									flagsEnable <= 5'b10100;
								end
							MOV  :  ALUSrcB <= 2'b11;
							AND   :  ALUOp <= 3'b000;
							XOR  :  ALUOp <= 3'b011;
							CMP  :  
							begin
							flagsEnable <= 5'b01011;
							regWrite <= 0;
							end
						endcase
					end
			end
			SHIFT:
			begin
				shiftImmediate <= ~extendedOpCode[2];
				shiftSelect <= 1;
				regWrite <= 1;
			end
			BCOND:
			begin
				ALUSrcA <= 0;
				ALUSrcB <= 2'b10;
				ALUOp <= 3'b010;
				PCEnable <= PC_branch;
			end
			
			LOADEX:
				begin				
					ALUOutSelect <= 1;
					memEnable <= 1;
					MemtoReg <= 1;
					regWrite <= 1;
				end
			blockRamWrite:
				begin
					ALUOutSelect <= 1;
					memEnable <= 1;
					MemtoReg <= 1;
					regWrite <= 1;
				end
				
			STOREX:
				begin
					ALUOutSelect <= 1;
					//memEnable <= 1;
					memEnable <= 1;
					memWrite <= 1;
					
				end
			JALEX:
				begin
					JALSelect <= 1;
					jump <= 1;
					regWrite <= 1;
					PCEnable <= 1;
				end
			
			
			JcondEX:
				begin
					jump <= 1;
					PCEnable <= PC_branch;
				end
			
			default
				begin
						ALUOp <= 3'b001;
						regWrite <= 0;
						memEnable <= 0;
						memWrite <= 0;
						ALUSrcA <= 1;
						ALUSrcB <= 2'b00;
						//branch_mux <= 0; // 1 = branch, 0 = jump
						IRWrite <= 0;
						MemtoReg <= 0;
						PCEnable <= 0;
						zeroExtend <= 0;
						shiftImmediate <= 0;
						ALUOutSelect <= 0;
						shiftSelect <= 0;
						flagsEnable <= 5'b00000;
						immedateALUSrcA <= 0;
						JALSelect <= 0;
						jump <= 0;
						LUIEnable <= 0;
				end

		endcase
	end
// The following always block is a copy of Table 1 in the ISA
// CondCode = extended opcode
always@(*) begin 					// Since the only instructions that use	
	case (cond)							// this are Bcond and Jcond we can assume
		4'b0000: PC_branch<=PSRCodes[1];		// that the PSR codes have all settled
		4'b0001: PC_branch<=~PSRCodes[1];	// down and are correct
		4'b1101: PC_branch<=(PSRCodes[0]|PSRCodes[1]);
		4'b0010: PC_branch<=PSRCodes[4];
		4'b0011: PC_branch<=~PSRCodes[4];
		4'b0100: PC_branch<=PSRCodes[3];
		4'b0101: PC_branch<=~PSRCodes[3];
		4'b1010: PC_branch<=(~PSRCodes[3]&~PSRCodes[1]);
		4'b1011: PC_branch<=(PSRCodes[3]|PSRCodes[1]);
		4'b0110: PC_branch<=PSRCodes[0];
		4'b0111: PC_branch<=~PSRCodes[0];
		4'b1000: PC_branch<=PSRCodes[2];
		4'b1001: PC_branch<=~PSRCodes[2];
		4'b1100: PC_branch<=(~PSRCodes[0]&~PSRCodes[1]);
		4'b1110: PC_branch<=1;
		4'b1111: PC_branch<=0;
		default: PC_branch<=0;
	endcase
end
endmodule 
